package game.Sprites;

import game.Utils.GraphicsUtils;
import game.Vector3;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

import javafx.scene.paint.Color;
import java.awt.geom.Rectangle2D;
import java.rmi.server.ExportException;


public class AnimatedSprite extends Sprite {
    SpriteSheet spriteSheet;
    final int totalFrames;
    final double width, height;
    final boolean looped; // Animation is a loop if true

    double animationTime;
    long frameDuration;

    int currentFrame;
    long millisTillNextFrame;

    public AnimatedSprite(Vector3 position, double rotation, double width, double height, Color color, SpriteSheet spriteSheet, double animationTime, boolean looped) throws Exception{
        // Instantiate Sprite class
        super(position, rotation, color);

        if(spriteSheet==null){
            System.out.println("Spritesheet is null");
        }
        // Animation cant last 0.0 seconds and sprite cannot null
        if(animationTime <= 0.0 || spriteSheet == null){
            throw new Exception();
        }

        // Instantiate parameters
        this.width = width;
        this.height = height;
        this.looped = looped;
        this.spriteSheet = spriteSheet;
        this.totalFrames = spriteSheet.getCols()*spriteSheet.getRows();

        // Initiate animation times
        this.animationTime = animationTime;
        this.frameDuration = Long.max(1,(long)(animationTime * 1000) / (totalFrames));

        // Initiate animation in frame 0
        this.currentFrame = 0;
        this.millisTillNextFrame = frameDuration;
    }


    public AnimatedSprite(Vector3 position, double rotation, double width, double height, Color color, SpriteSheet spriteSheet, double animationTime) throws Exception{
        // Defaults looped to true
        this(position, rotation, width, height, color, spriteSheet, animationTime, true);
    }

    public void setAnimationTime(double time){
        long oldFrameDuration = this.frameDuration;

        // Set new times for animation and frames
        this.animationTime = time;
        this.frameDuration = Long.max(1,(long)(animationTime * 1000) / (totalFrames));

        // Update current frame remaining time (scale)
        millisTillNextFrame *= (double)this.frameDuration/oldFrameDuration;
    }

    public void resetAnimation(){
        this.currentFrame = 0;
        this.millisTillNextFrame = frameDuration;
    }

    @Override
    public void draw(GraphicsContext g2D, Vector3 screenPos, double squareRes) {
        // Draw current frame sprite
        double theta = this.getRotation();

        // Sprite size in pixels
        int pixelWidth = (int)(width * squareRes);
        int pixelHeight = (int)(height * squareRes);

        //Draw
        GraphicsUtils.drawRotatedImage(g2D,
                spriteSheet.get(currentFrame), theta + Math.PI/2,
                (int) screenPos.x - (int) pixelWidth / 2,
                (int) screenPos.y - (int) pixelHeight / 2,
                (int) pixelWidth, (int) pixelHeight);
    }

    @Override
    public Rectangle2D.Double getBound() {
        // bounding rectangle
        return new Rectangle2D.Double(this.position.x - width/2, this.position.y - height/2, width, height);
    }

    @Override
    public void update(double deltaTime) {
        long millisPassed = (long)(deltaTime*1000);
        this.millisTillNextFrame -= millisPassed;

        int oldFrame = currentFrame; // Old frame will be needed to compare with new one

        // Advance as many frames as needed (might be more than 1)
        while(millisTillNextFrame <= 0){
            millisTillNextFrame += frameDuration;
            currentFrame = (currentFrame+1) % totalFrames;
        }

        // If is not looped and animation is beginning again, destroy
        if(!looped && currentFrame < oldFrame ){
            this.destroy();
        }
    }
}
